//
// Created by LiuYou on 2021/8/15.
//

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cassert>
#include <unistd.h>


/**
 * 同时接受普通数据和带外数据。
 */
int main(int argc, char** argv) {
    if (argc < 3) {
        printf( "用法: %s ip port\n", basename( argv[0] ));
        exit( 1 );
    }
    char* ip = argv[1];
    int port = atoi( argv[2] );

    sockaddr_in localAddress{};
    localAddress.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &localAddress.sin_addr );
    localAddress.sin_port = htons( port );

    int listenFd = socket( PF_INET, SOCK_STREAM, 0 );
    assert( listenFd >= 0 );
    int ret = bind( listenFd, (sockaddr*) &localAddress, sizeof( localAddress ));
    assert( ret != -1 );
    ret = listen( listenFd, 5 );
    assert( ret != -1 );

    sockaddr_in remoteAddress{};
    socklen_t socklen = sizeof( remoteAddress );
    int connectFd = accept( listenFd, (sockaddr*) &remoteAddress, &socklen );
    if (connectFd < 0) {
        perror( "accept()" );
        close( listenFd );
        exit( 1 );
    }

    char buffer[1024];
    fd_set readFds;
    fd_set exceptionFds;
    FD_ZERO( &readFds );
    FD_ZERO( &exceptionFds );

    while (true) {
        memset( buffer, '\0', sizeof( buffer ));
        FD_SET( connectFd, &readFds );
        FD_SET( connectFd, &exceptionFds );
        ret = select( connectFd + 1, &readFds, nullptr, &exceptionFds, nullptr );
        if (ret < 0) {
            perror( "select()" );
            break;
        }

        // 对于可读事件, 采用普通的 recv函数读取数据。
        if (FD_ISSET( connectFd, &readFds )) {
            ret = recv( connectFd, buffer, sizeof( buffer ) - 1, 0 );
            if (ret <= 0) {
                break;
            }
            printf( "从正常数据: %s 中得到了 %d 字节。\n", buffer, ret );
        } else if (FD_ISSET( connectFd, &exceptionFds )) {
            ret = recv( connectFd, buffer, sizeof( buffer ) - 1, MSG_OOB );
            if (ret <= 0) {
                break;
            }
            printf( "从oob数据: %s 中得到了 %d 字节。\n", buffer, ret );
        }

    }
    close( connectFd );
    close( listenFd );


    exit( 0 );
}